package pathways;

/**
 * Title:        Pathways
 * Description:  Pure Java version of the Pathways Project.
 * Copyright:    Copyright (c) 2002
 * Company:      Ants Technologies
 * @author Veejay Sani
 * @version 1.0
 */

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

import pathways.Database.Implementation.CDatabase;
import pathways.Database.Specialized.Visual.CDBSVSearchByEnzyme;
import pathways.Database.Specialized.Visual.CDBSVSearchBySubstrate;
import pathways.Database.Specialized.Visual.CSearchCompound;
import pathways.util.Debug.CDebug;

public class CSubstrateMouseListener implements ISubstrateMouseListener, ActionListener
{
  public CSubstrateMouseListener(CPathway pathway)
  {
    this.pathway = pathway;
    lastPoint = new Point(0,0);
  }

  public void mouseClicked(CSubstrateMouseEvent e)
  {
    int clickedPoint = getPointWithFocus(e);

    if (clickedPoint >= 0)
    {
      CSubstrate substrate = e.getSubstrate();
      CConnectionPoint p = substrate.getConnectionPoint(clickedPoint);

      if (e.getMouseEvent().getModifiers() == 4)
      {
        // right click
        lastConnectionPointRightClicked = p;
        mLastSubstrateClicked = e.getSubstrate();
        mLastClickedPointNum = clickedPoint;

        JPopupMenu popup = getPopupMenuForPoint(p,substrate);

        popup.show(pathway,e.getMouseEvent().getX(),e.getMouseEvent().getY());
      }
      else
      {
        // left click
        if (!p.isAnyBranchAttached())
          addReaction(e.getSubstrate(),clickedPoint);
      }
    }
  }

  private JPopupMenu getPopupMenuForPoint(CConnectionPoint p, CSubstrate sub)
  {
    JPopupMenu popup = new JPopupMenu(sub.getName());

    Vector v = p.getFirstReactionAtEachBranch();
    JMenuItem item = null;

    for (int i = 0; i < v.size(); i++)
    {
      if (p.getAttachedBranch() == i)
        item = new JMenuItem("Collapse Branch " + i);
      else
        item = new JMenuItem("Expand Branch " + i);

      item.addActionListener(this);

      popup.add(item);
    }

    if (!p.isAnyBranchAttached())
    {
      item = new JMenuItem("Add Missing Info Link");
      item.addActionListener(this);
      popup.add(item);
    }
    else
    {
      item = new JMenuItem("Telescope From");
      item.addActionListener(this);
      popup.add(item);
    }

    if (p.getReactionsLeadingToMe().size() > 0)
    {
      item = new JMenuItem("Telescope To");
      item.addActionListener(this);
      popup.add(item);
    }

    return popup;
  }

  public void mousePressed(CSubstrateMouseEvent e)
  {
    lastPoint = e.getMouseEvent().getPoint();
  }

  public void mouseReleased(CSubstrateMouseEvent e)
  {
    lastPoint.x = 0;
    lastPoint.y = 0;
  }

  public void mouseEntered(CSubstrateMouseEvent e)
  {

  }

  public void mouseExited(CSubstrateMouseEvent e)
  {

  }

  public void mouseDragged(CSubstrateMouseEvent e)
  {
    Point p = e.getMouseEvent().getPoint();
    Rectangle r = new Rectangle(e.getSubstrate().getRect());

    r.x += p.x - lastPoint.x;
    r.y += p.y - lastPoint.y;

    e.getSubstrate().getCoOrdinates().setRect(r);

    lastPoint = p;

    pathway.draw();
  }

  public void mouseMoved(CSubstrateMouseEvent e)
  {
    int focusPoint = getPointWithFocus(e);
    IPathwayPainter pathwayPainter = pathway.getPathwayPainter();

    if (focusPoint >= 0)
    {
      Rectangle rect = getRectAroundPoint(focusPoint,e.getSubstrate().getCoOrdinates().getPoint(focusPoint),getDimensionForSmallRect(e.getSubstrate()));

      pathwayPainter.setExtraRectToDraw(rect,Color.orange);

      //Component c = pathway.getGUIComponent();
      //Graphics g = c.getGraphics();

      pathway.draw();

      //pathwayPainter.setExtraRectToDraw(null,null);

      //Color color = g.getColor();
      //g.setColor(Color.orange);
      //g.fillRect(rect.x,rect.y,rect.width,rect.height);
      //g.setColor(color);
    }
    else
    {
      pathwayPainter.setExtraRectToDraw(null,Color.orange);
      pathway.draw();
    }
  }

  private int getPointWithFocus(CSubstrateMouseEvent e)
  {
    CCoOrdinates coords = e.getSubstrate().getCoOrdinates();
    Point p0 = coords.getPoint0();
    Point p1 = coords.getPoint1();
    Point p2 = coords.getPoint2();
    Point p3 = coords.getPoint3();

    Dimension dimension = getDimensionForSmallRect(e.getSubstrate());

    // always width of the little rect touches the bigger rect
    Rectangle rect0 = getRectAroundPoint(0,p0,dimension);
    Rectangle rect1 = getRectAroundPoint(1,p1,dimension);
    Rectangle rect2 = getRectAroundPoint(2,p2,dimension);
    Rectangle rect3 = getRectAroundPoint(3,p3,dimension);

    Point p = e.getMouseEvent().getPoint();

    int focusPoint = -1;

    if (isPointInRect(p,rect0))
      focusPoint = 0;
    else if (isPointInRect(p,rect1))
      focusPoint = 1;
    else if (isPointInRect(p,rect2))
      focusPoint = 2;
    else if (isPointInRect(p,rect3))
      focusPoint = 3;
    else
      focusPoint = -1; // nothing clicked

    return focusPoint;
  }

  private Rectangle getRectAroundPoint(int pointNum, Point p, Dimension rectDimension)
  {
    int height = rectDimension.height;
    int width = rectDimension.width;
    Rectangle rect = null;

    if (pointNum == 0)
    {
      rect = new Rectangle(p.x,p.y-width/2,height,width); // note height and width are switched
    }
    else if (pointNum == 1)
    {
      rect = new Rectangle(p.x-width/2,p.y,width,height);
    }
    else if (pointNum == 2)
    {
      rect = new Rectangle(p.x-height,p.y-width/2,height,width); // same as p0
    }
    else if (pointNum == 3)
    {
      rect = new Rectangle(p.x-width/2,p.y-height,width,height);
    }

    return rect;
  }

  private boolean isPointInRect(Point p, Rectangle r)
  {
    boolean isIn = false;

    if (p.x >= r.x && p.x <= (r.x + r.width)
        && p.y >= r.y && p.y <= (r.y + r.height))
    {
      isIn = true;
    }

    return isIn;
  }

  private void addReaction(CSubstrate subs, int connectFrom)
  {
    Rectangle rect = getNextRect(subs.getRect(),connectFrom);
    int connectTo = getConnectionTo(connectFrom);
    boolean bisSubstrateAsEnzyme = isSubstrateAsEnzyme(subs,pathway.getDatabase());
    CReaction rxn = null;

    if (bisSubstrateAsEnzyme)
    {
      int lnResponse = JOptionPane.showConfirmDialog(pathway.getOwner(),"Do you want to use this Substrate as an Enzyme?","Use Substrate as Enzyme?",JOptionPane.YES_NO_OPTION);

      if (lnResponse == JOptionPane.NO_OPTION)
          bisSubstrateAsEnzyme = false;
    }

    if (bisSubstrateAsEnzyme)
    {
      // get rectangles
      Rectangle rectSub1 = new Rectangle();
      Rectangle rectSub2 = new Rectangle();
      intWrapper connFrom = new intWrapper();
      intWrapper connTo = new intWrapper();

      calcRectanglesForSubstrateAsEznyme(subs,connectFrom,rectSub1,rectSub2,connFrom,connTo);

      //CDBSVSearchReaction searchFrm = new CDBSVSearchReaction(pathway.getDatabase(),pathway.getOwner(),rectSub1,rectSub2,connFrom.anInt,connTo.anInt);
      CDBSVSearchByEnzyme searchFrm = new CDBSVSearchByEnzyme(new CEnzyme(subs.getName(),subs.getID()),connFrom.anInt,connTo.anInt,rectSub2,rectSub1,pathway.getDatabase(),pathway.getOwner());

      searchFrm.show();

      rxn = searchFrm.getReaction();
    }
    else
    {
      int[] lnRxnIDs = getRxnIDsToIgnore(subs);

      CDBSVSearchBySubstrate searchForm = new CDBSVSearchBySubstrate(subs,connectFrom,connectTo,rect,pathway.getDatabase(),pathway.getOwner(),lnRxnIDs);

      searchForm.show();

      rxn = searchForm.getReaction();
    }

    //CEnzyme enzyme = new CEnzyme("CompoundXYZ",++dummyCount);
    //CSubstrate prod = new CSubstrate("CompoundZZZabcdefghijkl",++dummyCount,rect);
    //CReaction rxn = new CReaction(subs,enzyme,prod,connectFrom,connectTo,0);

    if (rxn != null)
    {
       if (bisSubstrateAsEnzyme)
       {
          pathway.addReaction(rxn);

          CSubstrateAsEnzyme subAsEnz = new CSubstrateAsEnzyme(subs,rxn.getEnzyme(),connectFrom,subs.getCoOrdinates().getPoint(connectFrom),connectTo);

          rxn.setSubAsEnzyme(subAsEnz);

          CConnectionPoint p = subs.getConnectionPoint(connectFrom);
          Vector v = p.getFirstReactionAtEachBranch();
          v.add(rxn);
          p.attachBranch(v.size()-1);

          pathway.getSubstratesAsEnzymes().add(subAsEnz);

          pathway.draw();
       }
       else
       {
          pathway.addReaction(rxn);
          pathway.draw();
       }
    }
  }

  private boolean isSubstrateAsEnzyme(CSubstrate s, CDatabase db)
  {
    // check if substrate occurs as enzyme
    return db.getSelect().isEnzyme((int)s.getID());
  }

  private int[] getRxnIDsToIgnore(CSubstrate sub)
  {
    Vector v = new Vector();

    v.addAll(sub.getConnectionPoint0().getReactionsLeadingToMe());
    v.addAll(sub.getConnectionPoint0().getFirstReactionAtEachBranch());

    v.addAll(sub.getConnectionPoint1().getReactionsLeadingToMe());
    v.addAll(sub.getConnectionPoint1().getFirstReactionAtEachBranch());

    v.addAll(sub.getConnectionPoint2().getReactionsLeadingToMe());
    v.addAll(sub.getConnectionPoint2().getFirstReactionAtEachBranch());

    v.addAll(sub.getConnectionPoint3().getReactionsLeadingToMe());
    v.addAll(sub.getConnectionPoint3().getFirstReactionAtEachBranch());

    //return (int)((CReaction)sub.getConnectionPoint0().getReactionsLeadingToMe().get(0)).getID();
    
    int[] rxnIDs = new int[v.size()];
    
    for (int i = 0; i < v.size(); i++)
    {
      CReaction rxn = (CReaction)v.get(i);
      
      rxnIDs[i] = (int)rxn.getID(); 
    }
    
    return rxnIDs;
  }

  private Rectangle getNextRect(Rectangle r, int connectFrom)
  {
    Rectangle rect = new Rectangle(0,0,0,0);

    if (connectFrom == 0)
    {
      rect = new Rectangle(r.x-2*r.width, r.y, r.width,r.height);
    }
    else if (connectFrom == 1)
    {
      rect = new Rectangle(r.x, r.y-2*r.height, r.width,r.height);
    }
    else if (connectFrom == 2)
    {
      rect = new Rectangle(r.x+2*r.width, r.y, r.width,r.height);
    }
    else if (connectFrom == 3)
    {
      rect = new Rectangle(r.x, r.y+2*r.height, r.width,r.height);
    }
    else
    {
      // raise exception
    }

    return rect;
  }

  private int getConnectionTo(int connectionFrom)
  {
    int connectionTo = 0;

    switch(connectionFrom)
    {
      case 0: connectionTo = 2; break;
      case 1: connectionTo = 3; break;
      case 2: connectionTo = 0; break;
      case 3: connectionTo = 1; break;
      default: // raise exception
    }

    return connectionTo;
  }

  private Dimension getDimensionForSmallRect(CSubstrate s)
  {
    Rectangle r = s.getCoOrdinates().getRect();

    return new Dimension(r.width/5,r.height/5);
  }

  public void actionPerformed(ActionEvent e)
  {
    // Right clicks on the points are handled here

    String command = e.getActionCommand();
    int branchNum = -1;

    if (command.equals("Add Missing Info Link"))
    {
       missingInfoLinkHandler();
       return;
    }

    if (command.equals("Telescope From"))
    {
      mfromTelescopingPoint = lastConnectionPointRightClicked;
      mfromTelescopingSubstrate = mLastSubstrateClicked;
      mfromTelescopingPointNum = mLastClickedPointNum;
      return;
    }

    if (command.equals("Telescope To"))
    {
      mfromTelescopingPoint.detachAll(lastConnectionPointRightClicked);
      CReaction lrxn = new CReaction(mfromTelescopingSubstrate,new CEnzyme("......",0),mLastSubstrateClicked,mfromTelescopingPointNum,mLastClickedPointNum,0);
      lrxn.setVisibility(true);
      pathway.addReaction(lrxn);
      pathway.draw();
      return;
    }

    try{
      branchNum = Integer.parseInt(command.substring(command.length()-1));
      CDebug.println(this,"BranchNum = " + branchNum);
    } catch(NumberFormatException exception)
    {
      CDebug.println(this,"popupMenu for point has unexpected format.");
    }

    if (branchNum >= 0)
    {
      // attach that branch
      if (lastConnectionPointRightClicked.getAttachedBranch() == branchNum)
      {
        lastConnectionPointRightClicked.detachAll(null);
      }
      else
      {
        lastConnectionPointRightClicked.attachBranch(branchNum);
      }

      pathway.draw();
    }
  }

  private void missingInfoLinkHandler()
  {
    // ask for a compound
    // create a rxn with fake enzyme - i.e. enzymeID = 0
    // also rxnID = 0

    CCompound c = searchCompound();

    if (c != null)
    {
      Rectangle rect = getNextRect(mLastSubstrateClicked.getRect(),mLastClickedPointNum);
      int connectTo = getConnectionTo(mLastClickedPointNum);

      CEnzyme enz = new CEnzyme("",0);
      CSubstrate prod = new CSubstrate(c.getName(),c.getID(),rect);

      CReaction rxn = new CReaction(mLastSubstrateClicked,enz,prod,mLastClickedPointNum,connectTo,0);

      pathway.addReaction(rxn);
      pathway.draw();
    }
  }

  private CCompound searchCompound()
  {
    CSearchCompound c = new CSearchCompound(true,pathway.getOwner(),pathway.getDatabase(),CSearchCompound.FILTER_NONE);

    c.show();

    return c.getSelectedCompound();
  }

  private void calcRectanglesForSubstrateAsEznyme(CSubstrate s, int connectingFrom, Rectangle r1, Rectangle r2, intWrapper connFrom, intWrapper connTo)
  {
    // connectFrom, connectTo are for the sub and prod of the reaction
    // that is being catalyzed by the substrateAsEnzyme

    Point p = s.getCoOrdinates().getPoint(connectingFrom);
    int width = s.getRect().width;
    int height = s.getRect().height;
    int lengthSubAsEnzArrow = 0;
    int connectFrom = 0;
    int connectTo = 0;

    r1.width = width;
    r1.height = height;
    r2.setSize(r1.getSize());

    if (connectingFrom == 0)
    {
      lengthSubAsEnzArrow = 2*width;

      r1.x = p.x - lengthSubAsEnzArrow;
      r1.y = p.y - 2*height;

      r2.x = r1.x;
      r2.y = p.y + height;

      connectFrom = 3;
      connectTo = 1;
    }
    else if (connectingFrom == 1)
    {
      lengthSubAsEnzArrow = 2*height;

      r1.x = p.x -2*width;
      r1.y = p.y - lengthSubAsEnzArrow;

      r2.x = p.x + width;
      r2.y = r1.y;

      connectFrom = 2;
      connectTo = 0;
    }
    else if (connectingFrom == 2)
    {
      lengthSubAsEnzArrow = width/2;

      r1.x = p.x + lengthSubAsEnzArrow;
      r1.y = p.y - 2*height;

      r2.x = r1.x;
      r2.y = p.y + height;

      connectFrom = 3;
      connectTo = 1;

    }
    else if (connectingFrom == 3)
    {
      lengthSubAsEnzArrow = height;

      r1.x = p.x -2*width;
      r1.y = p.y + lengthSubAsEnzArrow;

      r2.x = p.x + width;
      r2.y = r1.y;

      connectFrom = 2;
      connectTo = 0;
    }

    connFrom.anInt = connectFrom;
    connTo.anInt = connectTo;
  }

  private void collapse(CConnectionPoint from, CConnectionPoint to)
  {

  }

  class intWrapper
  {
    public int anInt;
  }

  private CPathway pathway;
  private Point lastPoint;
  private CConnectionPoint lastConnectionPointRightClicked;
  private CSubstrate mLastSubstrateClicked = null;
  private int mLastClickedPointNum;
  private int dummyCount = 4;
  private CConnectionPoint mfromTelescopingPoint = null;
  private CSubstrate mfromTelescopingSubstrate = null;
  private int mfromTelescopingPointNum = 0;
}